﻿#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//C语⾔引⼊了动态内存开辟，让程序员⾃⼰可以申请和释放空间，就⽐较灵活了。
//int main(void)
//{
//	int* p = (int*)malloc(10 * sizeof(int));
//	if (p == NULL)
//	{
//		perror(malloc);
//		return 1;
//	}
//	for (int i = 0; i < 10; i++)
//	{
//		*(p+i) = 1 + i;
//	}
//	return 0;
//}
/*
	局部数组：栈区
	动态内存：堆区
*/
//int main(void)
//{
//	/*	    X86环境下运行	      */	
//	int* p = (int*)malloc(INT_MAX);
//	if (p == NULL)
//	{
//		perror("malloc");
//		return 1;//函数提前结束思想
//	}
//	for (int i = 0; i < 10; i++)
//	{
//		*(p + i) = 1 + i;//p指向不变
//	}
//	
//	free(p);//p为野指针
//	p = NULL;//放在后面，放在前面FREE函数什么都不会做
//	return 0;
//}
//int main(void)
//{
//	int *p=(int *)calloc(10, sizeof(int));
//	if (p == NULL)
//	{
//		perror("calloc");
//		return 1;
//	}
//	for (int i = 0; i < 10; i++)
//	{
//		printf("%d\n", p[i]);
//	}
//	int* ptr = (int*)realloc(p, 20 * sizeof(int));//传入NULL相当于申请空间
//	if (ptr != NULL)
//	{
//		p = ptr;
//	}
//	free(p);
//	p = NULL;
//	return 0;
//}
//1.对NULL指针的解引⽤操作
//void test()
//{
//	int* p = (int*)malloc(INT_MAX / 4);
//	*p = 20;//如果p的值是NULL，就会有问题
//	free(p);
//}
//2.对动态开辟空间的越界访问
//void test()
//{
//	int i = 0;
//	int* p = (int*)malloc(10 * sizeof(int));
//	if (NULL == p)
//	{
//		exit(EXIT_FAILURE);
//	}
//	for (i = 0; i <= 10; i++)
//	{
//		*(p + i) = i;//当i是10的时候越界访问
//	}
//	free(p);
//}
//3.对⾮动态开辟内存使⽤free释放
//void test()
//{
//	int a = 10;
//	int* p = &a;
//	free(p);//ok?
//}
//4.使⽤free释放⼀块动态开辟内存的⼀部分
//void test()
//{
//	int* p = (int*)malloc(100);
//	p++;
//	free(p);//p不再指向动态内存的起始位置
//}
//5.对同⼀块动态内存多次释放
//void test()
//{
//	int* p = (int*)malloc(100);
//	free(p);
//	free(p);//重复释放，没有置NULL
//}
//6.动态开辟内存忘记释放（内存泄漏）
//void test()
//{
//	int* p = (int*)malloc(100);
//	if (NULL != p)
//	{
//		*p = 20;
//	}
//}
//int main()
//{
//	test();
//	while (1);
//}
//题目一
//void GetMemory(char** p)//传值调用与传地址的区别
//{
//	*p = (char*)malloc(100);//想办法避开空指针
//}
//void Test(void)
//{
//	char* str = NULL;
//	GetMemory(str);
//	strcpy(str, "hello world");
//	printf(str);
//}
//题目二
//char* GetMemory(void)
//{
//	char p[] = "hello world";
//	return p;//函数返回，空间销毁不属于你
// //返回值可以
//}
//void Test(void)
//{
//	char* str = NULL;
//	str = GetMemory();
//	printf(str);//可以直接传地址，这是本质
//}
//题目三
//void GetMemory(char** p, int num)
//{
//	*p = (char*)malloc(num);
//}
//void Test(void)
//{
//	char* str = NULL;
//	GetMemory(&str, 100);
//	strcpy(str, "hello");
//	printf(str);
//	//内存泄露
//}
//题目四
//void Test(void)
//{
//	char* str = (char*)malloc(100);
//	strcpy(str, "hello");
//	free(str);
//	if (str != NULL)//野指针非法访问
//	{
//		strcpy(str, "world");
//		printf(str);
//	}
//}

//struct st_type
//{
//	int i;
//	int a[];//柔性数组成员 有的编译器支持a[0]
//};
/*
	柔性数组的特点：
	• 结构中的柔性数组成员前⾯必须⾄少⼀个其他成员。
	• sizeof 返回的这种结构⼤⼩不包括柔性数组的内存。
	• 包含柔性数组成员的结构⽤malloc()函数进⾏内存的动态分配，并且分配的内存应该⼤于结构的⼤
	⼩，以适应柔性数组的预期⼤⼩。
*/
//typedef struct st_type
//{
//	int i;
//	int a[0];//柔性数组成员
//}type_a;
//int main()
//{
//	int i = 0;
//	type_a* p = (type_a*)malloc(sizeof(type_a) + 100 * sizeof(int));
//	//业务处理
//	p->i = 100;
//	for (i = 0; i < 100; i++)
//	{
//		p->a[i] = i;
//	}
//	free(p);
//	return 0;
//}
//typedef struct st_type
//{
//	int i;
//	int* p_a;
//}type_a;
//int main()
//{
//	type_a* p = (type_a*)malloc(sizeof(type_a));
//	p->i = 100;
//	p->p_a = (int*)malloc(p->i * sizeof(int));
//
//	//业务处理
//	for (int i = 0; i < 100; i++)
//	{
//		p->p_a[i] = i;
//	}
//	//释放空间
//	free(p->p_a);//顺序
//	p->p_a = NULL;
//	free(p);
//	p = NULL;
//	return 0;
//}
/*
		⽅法1 的实现有两个好处：
		第⼀个好处是：⽅便内存释放
		如果我们的代码是在⼀个给别⼈⽤的函数中，你在⾥⾯做了⼆次内存分配，并把整个结构体返回给⽤
		⼾。⽤⼾调⽤free可以释放结构体，但是⽤⼾并不知道这个结构体内的成员也需要free，所以你不能
		指望⽤⼾来发现这个事。所以，如果我们把结构体的内存以及其成员要的内存⼀次性分配好了，并返
		回给⽤⼾⼀个结构体指针，⽤⼾做⼀次free就可以把所有的内存也给释放掉。
		第⼆个好处是：这样有利于访问速度.
		连续的内存有益于提⾼访问速度，也有益于减少内存碎⽚。（其实，我个⼈觉得也没多⾼了，反正你
		跑不了要⽤做偏移量的加法来寻址）

*/
/*
	C/C++程序内存分配的⼏个区域：
	1. 栈区（stack）：在执⾏函数时，函数内局部变量的存储单元都可以在栈上创建，函数执⾏结束时
	这些存储单元⾃动被释放。栈内存分配运算内置于处理器的指令集中，效率很⾼，但是分配的内
	存容量有限。 栈区主要存放运⾏函数⽽分配的局部变量、函数参数、返回数据、返回地址等。
	《函数栈帧的创建和销毁》
	2. 堆区（heap）：⼀般由程序员分配释放， 若程序员不释放，程序结束时可能由OS回收 。分配⽅
	式类似于链表。
	3. 数据段（静态区）：（static）存放全局变量、静态数据。程序结束后由系统释放。
	4. 代码段：存放函数体（类成员函数和全局函数）的⼆进制代码。
*/